﻿using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using YAFTS.Contracts;
using YAFTS.Contracts.Model;
using YAFTS.Core.Model;

namespace YAFTS.Core
{
	internal sealed class IndexStorage : IIndexStorage
	{
		private readonly ConcurrentDictionary<IFile, List<IWord>> _files = new ConcurrentDictionary<IFile, List<IWord>>();
		private readonly ConcurrentDictionary<IWord, List<IFile>> _words = new ConcurrentDictionary<IWord, List<IFile>>();

		public IEnumerable<IWord> GetFileWords(string fileName)
		{
			List<IWord> words;
			if (!_files.TryGetValue(new File(fileName), out words))
			{
				return Enumerable.Empty<IWord>();
			}

			lock (words)
			{
				return words.ToArray();
			}
		}

		public void DeleteFromIndex(IEnumerable<IWord> words, string fileName)
		{
			List<IWord> fileWords;
			var file = new File(fileName);
			if (!_files.TryGetValue(file, out fileWords))
			{
				return;
			}

			lock (fileWords)
			{
				foreach (var word in words)
				{
					List<IFile> files;
					if (_words.TryGetValue(word, out files))
					{
						lock (files)
						{
							files.Remove(file);
							if (files.Count == 0)
							{
								_words.TryRemove(word, out files);
							}
						}
					}

					fileWords.Remove(word);
					if (fileWords.Count == 0)
					{
						_files.TryRemove(file, out fileWords);
					}
				}
			}
		}

		public void AddToIndex(IEnumerable<IWord> words, string fileName)
		{
			var file = new File(fileName);
			var fileWords = _files.GetOrAdd(file, new List<IWord>());


			foreach (var word in words)
			{
				var files = _words.GetOrAdd(word, new List<IFile>());
				lock (files)
				{
					files.Add(file);
				}
				lock (fileWords)
				{
					fileWords.Add(word);
				}
			}
		}

		public IEnumerable<IFile> GetWordFiles(string word)
		{
			List<IFile> res;

			return !_words.TryGetValue(new Word(word), out res) ? Enumerable.Empty<IFile>() : res.ToArray();
		}

		public void Clear()
		{
			_files.Clear();
			_words.Clear();
		}

		public void RenameFile(string oldPath, string newPath)
		{
			var file = _files.Keys.FirstOrDefault(x => x.Name == oldPath);
			if (file == null)
			{
				return;
			}

			List<IWord> words;
			_files.TryRemove(file, out words);

			file.Rename(newPath);

			_files.TryAdd(file, words);
		}
	}
}